package com.ecr.hub.front.bean.managed;

import java.util.List;

import javax.annotation.Resource;
import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.validator.ValidatorException;

import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Controller;

import com.ecr.hub.ApplicationException;
import com.ecr.hub.front.bean.AbstractManagedBean;
import com.ecr.hub.model.enumeration.HubUserRole;
import com.ecr.hub.model.user.Role;
import com.ecr.hub.model.user.User;
import com.ecr.hub.service.user.IUserManager;
import com.ecr.hub.util.EncryptionUtils;
import com.ecr.hub.util.ManagedBeanUtils;
import com.ecr.hub.util.MessageUtils;

/**
 * Cette classe est uniquement utilisée pour la connexion utilisateur, et la
 * modification du mot de passe à la première connexion. Toutes les autres
 * opérations sur un utilisateur sont effectuées par le bean qui gère les
 * utilisateurs.
 */
@Controller("loginManaged")
@Scope("session")
public class LoginManagedBean extends AbstractManagedBean {

	private static final long serialVersionUID = 1L;

	public static final String LOGIN_BEAN = "loginManaged";

	// Affiche la barre des menus en fonction de la connexion utilisateur.
	private boolean displayApplicationToolbar;
	// Affiche le bouton de validation pour la connexion
	private boolean displayConnectButton;
	// Le login pour l'utilisateur.
	private String login;
	// Le mot de passe pour l'utilisateur.
	private String password;
	// Confirmation du mot de passe utilisateur.
	private String passwordConfirm;
	// Utilisateur connecté à l'application.
	private User currentUser;

	@Resource(name = "userManager")
	private transient IUserManager userManager;

	/**
	 * Constructeur.
	 */
	LoginManagedBean() {

		setDisplayApplicationToolbar(false);
		setDisplayConnectButton(true);
		setDisplayMessage(false);
	}

	// ***************************
	// Les méthodes.
	// ***************************

	/**
	 * Recherche de l'utilisateur à l'aide de son login (nom utilisateur) et de
	 * son mot de passe. Si l'utilisateur est trouvé, il est alors routé vers la
	 * page de visualisation du Hub ou celle de modification du mot de passe
	 * s'il s'agit de la première connexion. Sinon, retour vers la page de
	 * connexion avec un message d'erreur. On part sur une hypothèse optimiste.
	 */
	public String login() {

		try {
			setCurrentUser(userManager.connect(login, password));
			setDisplayApplicationToolbar(true);
			String fromOutcome = "login_success";

			if (null == getCurrentUser()) {
				MessageUtils.setMessageError("login.notfound");
				setDisplayApplicationToolbar(false);
				fromOutcome = null;
			} else if (getCurrentUser().isAskForPassword()) {
				setDisplayApplicationToolbar(false);
				fromOutcome = "login_success_new_password";
			} else if (getCurrentUser().isUsersAlreadyConnected()) {
				setDisplayApplicationToolbar(false);
				MessageUtils.setMessageError("login.consult.only");
				setDisplayConnectButton(false);
				fromOutcome = null;
			}
			return fromOutcome;
		} catch (Exception e) {
			getErrorHandler(e);
			return null;
		}
	}

	/**
	 * Dans le cas ou plusieurs utilisateurs sont connectés en même temps et
	 * qu'ils ont des droits en modification, le nouvel arrivant à le choix de
	 * pouvoir se connecter en consultation uniquement. On change le rôle de
	 * l'utilisateur et on le route sur la page de visualisation du hub.
	 */
	public String loginConsult() {

		setDisplayApplicationToolbar(true);
		User user = getCurrentUser();
		Role role = new Role();
		role.setRole(HubUserRole.ROLE_ECR_CONSULTANT);
		user.setRole(role);
		return "login_success";
	}

	/**
	 * Dans le cas ou plusieurs utilisateurs sont connectés en même temps et
	 * qu'ils ont des droits en modification, le nouvel arrivant à le choix de
	 * deconnecter les autres utilisteurs pour prendre la main. On deconnecte
	 * les autres utilisateurs et on le route sur la page de visualisation du
	 * hub.
	 */
	public String loginForce() {

		try {
			setDisplayApplicationToolbar(true);
			userManager.force(getCurrentUser());
			return "login_success";
		} catch (Exception e) {
			getErrorHandler(e);
			return null;
		}
	}

	/**
	 * Deconnexion de l'application, on vide la session, on efface la barre des
	 * menus et on renvoi l'utilisateur sur la mire de connexion.
	 */
	public String logindDisconnect() {

		try {
			setDisplayApplicationToolbar(false);
			userManager.disconnect(getCurrentUser());
			return "global_disconnect";
		} catch (Exception e) {
			getErrorHandler(e);
			return null;
		}
	}

	/**
	 * A la première connexion utilisateur, mise à jour du mot de passe afin de
	 * préserver la confidentialité de la connexion par rapport au chef
	 * d'agence. A priori rien à faire hormis la mise à jour, on transfert
	 * l'utilisateur vers la page de visualisation du Hub.
	 */
	public String updatePassword() {

		try {
			setDisplayApplicationToolbar(true);
			getCurrentUser().setPassword(password);
			userManager.updatePassword(getCurrentUser());
			return "new_password_success";
		} catch (Exception e) {
			getErrorHandler(e);
			return null;
		}
	}

	/**
	 * Validator interne au bean. Vérification du mot de passe, ce dernier doit
	 * être identique à la confirmation du mot de passe. De plus, il doit être
	 * unique dans la base. Ce validator est utilisé par les utilisateurs à la
	 * première connexion à l'application.
	 */
	public void passwordUniqueValidator(FacesContext context,
			UIComponent component, Object value) throws ValidatorException,
			ApplicationException {
		List<User> lstUsers = userManager.getAllList();
		String encryptPassword = EncryptionUtils.encryptePassword(
				value.toString(), currentUser.getLogin());
		for (User user : lstUsers) {
			if ((user.getId() != currentUser.getId())
					&& (user.getPassword().equals(encryptPassword))) {
				throw new ValidatorException(
						MessageUtils
								.getMessageError("validator.password.notunique"));
			}
		}
	}

	/**
	 * Cette méthode est très importante pour l'ensemble de l'application. Elle
	 * permet à tout momment de récupérer l'utilisateur qui est connecté
	 */
	public static User getConnectedUser() throws ApplicationException {

		LoginManagedBean login = (LoginManagedBean) ManagedBeanUtils
				.getExternalContext().getSessionMap().get(LOGIN_BEAN);
		if (null != login && login.getCurrentUser() != null) {
			return login.getCurrentUser();
		} else {
			throw new ApplicationException(
					"Hub : impossible de récupérer l'utilisateur en session.");
		}
	}

	// ****************************
	// Les getters et setters.
	// ****************************

	public String getLogin() {
		return login;
	}

	public String getPassword() {
		return password;
	}

	public void setLogin(String login) {
		this.login = login;
	}

	public void setPassword(String password) {
		this.password = password;
	}

	public String getPasswordConfirm() {
		return passwordConfirm;
	}

	public void setPasswordConfirm(String passwordConfirm) {
		this.passwordConfirm = passwordConfirm;
	}

	public User getCurrentUser() {
		return currentUser;
	}

	public void setCurrentUser(User currentUser) {
		this.currentUser = currentUser;
	}

	public boolean isDisplayApplicationToolbar() {
		return displayApplicationToolbar;
	}

	public void setDisplayApplicationToolbar(boolean displayApplicationToolbar) {
		this.displayApplicationToolbar = displayApplicationToolbar;
	}

	public boolean isDisplayConnectButton() {
		return displayConnectButton;
	}

	public void setDisplayConnectButton(boolean displayConnectButton) {
		this.displayConnectButton = displayConnectButton;
	}
}
